// Copyright (c) 2006 Zorm
// Released under the MIT License

#ifndef NIDSPARSER_H
#define NIDSPARSER_H
#pragma once

#pragma pack(1)

class SecondTime {

public:
	SecondTime() {}
	SecondTime(int seconds) { SetSeconds(seconds); }
	void SetSeconds(int seconds) {
		m_seconds = seconds % 60;
		seconds /= 60;
		m_minutes = seconds % 60;
		seconds /= 60;
		m_hours = seconds % 24;
	}
	short GetSeconds() { return m_seconds; }
	short GetMinutes() { return m_minutes; }
	short GetHours() { return m_hours; }
	
private:
	short m_seconds;
	short m_minutes;
	short m_hours;
	
};

class JulianDate {

public:
	JulianDate() {}
	JulianDate(double date) { SetDate(date); }
	void SetDate(double julianDate) {
		int z = 0, a = 0, b = 0, c = 0;
		double  r = 0, g = 0, t = 0;
		t = julianDate - 1721118.5;
		z = (int)floor(t);
		r = t - z;
		g = z - 0.25;
		
		a = (int)floor(g / 36524.25);
		b = a - (int)floor((double)a / 4);
		m_year =  (int)floor((b + g) / 365.25);
		c = b + z - (int)floor(365.25 * m_year);
		m_month = (int)floor((double)(5 * c + 456) / 153);
		m_day = c - (int)floor((double)(153 * m_month - 457) / 5) + (int)r;
		if (m_month > 12) {
			m_year++;
			m_month -= 12;
		}
	}
	short GetDay() { return m_day; }
	short GetMonth() { return m_month; }
	short GetYear() { return m_year; }

	
private:
	short m_day;
	short m_month;
	short m_year;
};

struct PointLatLonR {
	float lat[4];
	float lon[4];
	unsigned char level;
};

struct BasicNIDSHeader {
	__int16 messageCode;
	__int16 messageDate;
	__int32 messageTime;
	__int32 messageLength;
	__int16 sourceID;
	__int16 destID;
	__int16 numberOfBlocks;
};

struct NIDSHeader {
	short messageCode;
	JulianDate messageDate;
	SecondTime messageTime;
	int messageLength;
	short sourceID;
	short destID;
	short numberOfBlocks;
};

struct BasicNIDSProductDescription {
	__int16 blockDivider;
	__int32 latitude;
	__int32 longitude;
	__int16 height;
	__int16 productCode;
	__int16 operationalMode;
	__int16 volumeCoveragePattern;
	__int16 sequenceNumber;
	__int16 volumeScanNumber;
	__int16 volumeScanDate;
	__int32 volumeScanStartTime;
	__int16 generationDate;
	__int32 generationTime;
	__int16 productDependent1;
	__int16 productDependent2;
	__int16 elevationNumber;
	__int16 productDependent3;
	__int16 dataLevel1;
	__int16 dataLevel2;
	__int16 dataLevel3;
	__int16 dataLevel4;
	__int16 dataLevel5;
	__int16 dataLevel6;
	__int16 dataLevel7;
	__int16 dataLevel8;
	__int16 dataLevel9;
	__int16 dataLevel10;
	__int16 dataLevel11;
	__int16 dataLevel12;
	__int16 dataLevel13;
	__int16 dataLevel14;
	__int16 dataLevel15;
	__int16 dataLevel16;
	__int16 productDependent4;
	__int16 productDependent5;
	__int16 productDependent6;
	__int16 productDependent7;
	__int16 productDependent8;
	__int16 productDependent9;
	__int16 productDependent10;
	__int16 numberOfMaps;
	__int32 offsetSymbology;
	__int32 offsetGraphic;
	__int32 offsetTabular;
};

struct NIDSProductDescription {
	short blockDivider;
	double latitude;
	double longitude;
	short height;
	short productCode;
	short operationalMode;
	short volumeCoveragePattern;
	short sequenceNumber;
	short volumeScanNumber;
	short volumeScanDate;
	int volumeScanStartTime;
	short generationDate;
	int generationTime;
	short productDependent1;
	short productDependent2;
	short elevationNumber;
	short productDependent3;
	short dataLevel1;
	short dataLevel2;
	short dataLevel3;
	short dataLevel4;
	short dataLevel5;
	short dataLevel6;
	short dataLevel7;
	short dataLevel8;
	short dataLevel9;
	short dataLevel10;
	short dataLevel11;
	short dataLevel12;
	short dataLevel13;
	short dataLevel14;
	short dataLevel15;
	short dataLevel16;
	short productDependent4;
	short productDependent5;
	short productDependent6;
	short productDependent7;
	short productDependent8;
	short productDependent9;
	short productDependent10;
	short numberOfMaps;
	int offsetSymbology;
	int offsetGraphic;
	int offsetTabular;
};

struct BasicNIDSSymbologyBlock {
	struct {
		__int16 blockDivider;
		__int16 blockID;
		__int32 blockLength;
		__int16 numberOfLayers;
	} Header;
	
};

struct NIDSSymbologyBlock {
	struct {
		short blockDivider;
		short blockID;
		int blockLength;
		short numberOfLayers;
	} Header;
	
};

struct BasicNIDSGraphicBlock {
	struct {
		__int16 blockDivider;
		__int16 blockID;
		__int32 blockLength;
		__int16 numberOfPages;
	} Header;
	
};

struct NIDSGraphicBlock {
	struct {
		short blockDivider;
		short blockID;
		int blockLength;
		short numberOfPages;
	} Header;
	
};

struct BasicNIDSGraphicPage {
	__int16 pageNumber;
	__int16 pageLength;
};

struct BasicNIDSSymbologyLayer {
	__int16 layerDivider;
	__int32 layerLength;
};

class NIDSParser {

public:
	NIDSParser(std::string filename) : m_file(filename) { LoadFile(); ParseFile(); }
	~NIDSParser() { if (m_fileHandle != INVALID_HANDLE_VALUE) { CloseHandle(m_fileHandle); } }
	bool LoadFile();
	bool ParseFile();
	NIDSProductDescription GetProductDescription() { return m_productDescription; }
	
private:
	bool ReadSymbologyBlock();
	bool ReadGraphicBlock();
	bool ReadTabularBlock();
	
	bool ParsePacket(unsigned char *packetData, short length);

	bool TranslateHeader(BasicNIDSHeader *header);
	bool TranslateProductDescription(BasicNIDSProductDescription *desc);
	bool TranslateSymbologyHeader(BasicNIDSSymbologyBlock *header);
	bool TranslateGraphicHeader(BasicNIDSGraphicBlock *header);

	FORCEINLINE float DegToRad(float deg);

	HANDLE m_fileHandle;
	std::string m_file;
	int m_startData;
	
	NIDSHeader m_header;
	NIDSProductDescription m_productDescription;
	NIDSSymbologyBlock m_symbologyBlock;
	NIDSGraphicBlock m_graphicBlock;
	
	enum PacketID {
		WRITESPECIALSYMBOL		= 0x0002,
		MESO					= 0x0003,
		WRITETEXTUNIFORM		= 0x0008,
		UNLINKEDVECTORUNIFORM	= 0x000a,
		SHEAR					= 0x000b,
		TVS						= 0x000c,
		HAILPOSITIVE			= 0x000d,
		HAILPROBABLE			= 0x000e,
		STORMID					= 0x000f,
		HDAHAIL					= 0x0013,
		RADIAL					= 0xaf1f
	};

};

#endif